﻿/*
 * copyright (c) 2009 hvbird
 *
 * This file is part of tdlib.
 *
 * tdlib is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * tdlib is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

using System;
using System.Collections;
using System.Text;
using System.Web;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Diagnostics;
using System.Xml;

namespace TDLib.Web
{
    /// <summary>
    /// 
    /// </summary>
    public class NetHttp
    {
        #region 成员变量

        string cPostData = "";				//提交的数据
        int nConnectTimeout = 120;			//超时
        string cUserAgent = "Mozilla/4.0";	//用户代理
        bool bHandleReferer = true;			//自动操作引用页
        string cReferer = "";				//引用页
        string cMethod = "POST";			//提交模式POST or GET
        string cUsername = "";
        string cPassword = "";
        string cProxyAddress = "";
        string cProxyBypass = "";
        string cProxyUsername = "";
        string cProxyPassword = "";
        bool bThrowExceptions = true;		//是否抛出异常
        bool bHandleCookies = true;			//自动操作Cookies
        CookieCollection oCookies;
        string cErrorMsg = "";				//错误返回
        bool bError = false;
        string cEncoding = "GB2312";		//UTF-8   GB2312
        bool bLocation = false;
        string strUrlHeader = string.Empty; //返回的HTTP头信息
        bool bAllowAutoRedirect = true; //这里不允许302 

        #endregion

        #region 构造函数
        public NetHttp() { }
        #endregion

        #region 属性值
        /// <summary>
        /// 登陆用户名
        /// </summary>
        public string Username
        {
            get { return this.cUsername; }
            set { cUsername = value; }
        }

        /// <summary>
        /// 登陆密码
        /// </summary>
        public string Password
        {
            get { return this.cPassword; }
            set { this.cPassword = value; }
        }

        /// <summary>
        /// 代理服务器地址
        /// </summary>
        public string ProxyAddress
        {
            get { return this.cProxyAddress; }
            set { this.cProxyAddress = value; }
        }

        /// <summary>
        /// 分号分割带来服务器地址
        /// </summary>
        public string ProxyBypass
        {
            get { return this.cProxyBypass; }
            set { this.cProxyBypass = value; }
        }

        /// <summary>
        /// 登陆代理服务器用户名
        /// </summary>
        public string ProxyUsername
        {
            get { return this.cProxyUsername; }
            set { this.cProxyUsername = value; }
        }
        /// <summary>
        /// 登陆代理服务器密码
        /// </summary>
        public string ProxyPassword
        {
            get { return this.cProxyPassword; }
            set { this.cProxyPassword = value; }
        }

        /// <summary>
        /// 设置访问超时时间
        /// 默认30妙
        /// </summary>
        public int Timeout
        {
            get { return this.nConnectTimeout; }
            set { this.nConnectTimeout = value; }
        }

        /// <summary>
        /// 是否自动操作引用页
        /// </summary>
        public bool HandleReferer
        {
            get { return this.bHandleReferer; }
            set { this.bHandleReferer = value; }
        }

        /// <summary>
        /// 引用页
        /// </summary>
        public string Referer
        {
            get { return this.cReferer; }
            set { this.cReferer = value; }
        }

        /// <summary>
        /// 提交模式，默认是POST,用GET模式的时候不能使用PostData
        /// </summary>
        /// <value></value>
        public string Method
        {
            get { return this.cMethod; }
            set { this.cMethod = value; }
        }

        /// <summary>
        /// 返回错误信息标记
        /// </summary>
        public string ErrorMsg
        {
            get { return this.cErrorMsg; }
            set { this.cErrorMsg = value; }
        }

        /// <summary>
        /// 错误标记
        /// </summary>
        public bool Error
        {
            get { return this.bError; }
            set { this.bError = value; }
        }

        /// <summary>
        /// 是否抛出异常
        /// </summary>
        public bool ThrowExceptions
        {
            get { return bThrowExceptions; }
            set { this.bThrowExceptions = value; }
        }

        /// <summary>
        /// 设置COOKE值
        /// </summary>
        public bool HandleCookies
        {
            get { return this.bHandleCookies; }
            set { this.bHandleCookies = value; }
        }

        /// <summary>
        /// Cookies集合
        /// </summary>
        public CookieCollection Cookies
        {
            get { return this.oCookies; }
            set { this.Cookies = value; }
        }

        /// <summary>
        /// 默认的编码
        /// </summary>
        public string MyEncoding
        {
            get { return this.cEncoding; }
            set { this.cEncoding = value; }
        }

        /// <summary>
        /// 自动跳转到新的页面
        /// </summary>
        public bool Location
        {
            get { return this.bLocation; }
            set { this.bLocation = value; }
        }

        /// <summary>
        /// 是否允许302
        /// </summary>
        public bool AllowAutoRedirect
        {
            get { return bAllowAutoRedirect; }
            set { bAllowAutoRedirect = value; }
        }

        /// <summary>
        /// 客户端头
        /// </summary>
        public string Agent
        {
            get { return cUserAgent; }
            set { cUserAgent = value; }
        }


        #endregion

        #region 只读属性值
        /// <summary>
        /// 获得POST/GET页Http头
        /// </summary>
        public string UrlHeader
        {
            get
            {
                return this.strUrlHeader;
            }
        }
        #endregion

        #region 对外提供的函数

        /// <summary>
        /// 增加提交的值
        /// 接收POST页面获取方法System.Web.HttpContext.Current.Request.Form[Key]
        /// </summary>
        /// <param name="Key"></param>
        /// <param name="Value"></param>
        public void AddPostKey(string Key, string Value)
        {
            //WinForm中System.Web.HttpUtility不可用暂时注释
            cPostData += Key + "=" + System.Web.HttpUtility.UrlEncode(Value, System.Text.Encoding.GetEncoding(cEncoding)) + "&";
        }

        /// <summary>
        /// 增加提交的连续值（完整或者部分完整值）
        /// </summary>
        /// <param name="FullPostBuffer"></param>
        public void AddPostKey(string FullPostBuffer)
        {
            cPostData += FullPostBuffer;
        }

        /// <summary>
        /// 返回POST/GET的信息
        /// </summary>
        /// <param name="Url"></param>
        /// <returns></returns>
        public string GetUrl(string Url)
        {
            return GetUrl(Url, "", "", null);
        }

        public bool SavePhotoFromUrl(string FileName, string Url)
        {
            bool Value = false;
            WebResponse response = null;
            Stream stream = null;

            try
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);

                response = request.GetResponse();
                stream = response.GetResponseStream();

                if (!response.ContentType.ToLower().StartsWith("text/"))
                {
                    Value = SaveBinaryFile(response, FileName);

                }

            }
            catch (Exception err)
            {
                string aa = err.ToString();
            }
            return Value;
        }
        /// <summary>
        /// Save a binary file to disk.
        /// </summary>
        /// <param name="response">The response used to save the file</param>
        // 将二进制文件保存到磁盘
        private bool SaveBinaryFile(WebResponse response, string FileName)
        {
            bool Value = true;
            byte[] buffer = new byte[1024];

            try
            {
                if (File.Exists(FileName))
                    File.Delete(FileName);
                Stream outStream = System.IO.File.Create(FileName);
                Stream inStream = response.GetResponseStream();

                int l;
                do
                {
                    l = inStream.Read(buffer, 0, buffer.Length);
                    if (l > 0)
                        outStream.Write(buffer, 0, l);
                }
                while (l > 0);

                outStream.Close();
                inStream.Close();
            }
            catch
            {
                Value = false;
            }
            return Value;
        }

        /// <summary>
        /// 返回POST/GET的信息
        /// </summary>
        /// <param name="Url"></param>
        /// <param name="UserName"></param>
        /// <param name="Password"></param>
        /// <param name="xmlDoc"></param>
        /// <returns></returns>
        public string GetUrl(string Url, string UserName, string Password, XmlDocument xmlDoc)
        {
            if (xmlDoc != null && xmlDoc.InnerXml != "")
                this.cPostData = xmlDoc.InnerXml;

            if(UserName != null && UserName.Length>0)
                this.cUsername = UserName;

            if(Password != null && Password.Length>0)
                this.cPassword = Password;

            Url = UrlEncode(Url);
            Debug.WriteLine(Url);
            try
            {
                this.bError = false;
                this.cErrorMsg = "";

                //通用的属性

                //使用 WebRequest.Create 方法初始化 HttpWebRequest 的一个新实例。
                //如果 URI 的方案是 http:// 或 https://，则 Create 将返回 HttpWebRequest 实例。

                HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(Url);
                Request.UserAgent = this.cUserAgent;
                Request.Timeout = this.nConnectTimeout * 1000;
                Request.Accept = "*/*";
                Request.AllowAutoRedirect = bAllowAutoRedirect;            

                Request.Referer = this.cReferer;

                //Request.Connection = "keep-alive";

                // 需要安全验证的访问
                if (this.cUsername.Length > 0)
                {
                    if (this.cUsername == "AUTOLOGIN")
                        Request.Credentials = CredentialCache.DefaultCredentials;
                    else
                        Request.Credentials = new NetworkCredential(this.cUsername, this.cPassword);
                }

                // 需要使用Proxy(代理服务器)和其配置
                if (this.cProxyAddress.Length > 0)
                {
                    if (this.cProxyAddress == "DEFAULTPROXY")
                    {
                        Request.Proxy = new WebProxy();
                        Request.Proxy = WebProxy.GetDefaultProxy();
                    }
                    else
                    {
                        WebProxy loProxy = new WebProxy(this.cProxyAddress, true);
                        if (this.cProxyBypass.Length > 0)
                        {
                            loProxy.BypassList = this.cProxyBypass.Split(';');
                        }

                        if (this.cProxyUsername.Length > 0)
                            loProxy.Credentials = new NetworkCredential(this.cProxyUsername, this.cProxyPassword);

                        Request.Proxy = loProxy;
                    }
                }

                // 需要操作Cookies和自动重用Cookies
                if (this.bHandleCookies)
                {
                    Request.CookieContainer = new CookieContainer();
                    if (this.oCookies != null && this.oCookies.Count > 0)
                    {
                        Request.CookieContainer.Add(this.oCookies);
                    }
                }

                Request.Method = cMethod;//设置提交模式

                if (this.cMethod == "POST")
                {
                    //内容类型
                    Request.ContentType = "application/x-www-form-urlencoded";//作为表单请求
                    //Request.ContentType = "text/html";

                    if (this.cPostData.EndsWith("&"))
                        this.cPostData = this.cPostData.Substring(0, this.cPostData.Length - 1);

                    //将URL编码后的字符串转化为字节 
                    byte[] lbPostBuffer = System.Text.Encoding.GetEncoding(cEncoding).GetBytes(cPostData);

                    //设置请求的ContentLength
                    Request.ContentLength = lbPostBuffer.Length;

                    //获得请求流
                    Stream loPostData = Request.GetRequestStream();

                    //将请求参数写入流
                    loPostData.Write(lbPostBuffer, 0, lbPostBuffer.Length);
                    loPostData.Close();

                    // *** clear the POST buffer
                    this.cPostData = "";
                }

                // *** Retrieve the response headers 

                //获得响应流
                HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();

                strUrlHeader = Response.Headers.ToString();

                // ** Save cookies the server sends

                if (this.bHandleCookies)
                {
                    if (Response.Cookies.Count > 0)
                    {
                        if (this.oCookies == null)
                        {
                            this.oCookies = Response.Cookies;
                        }
                        else
                        {
                            // ** If we already have cookies update the list
                            foreach (Cookie oRespCookie in Response.Cookies)
                            {
                                bool bMatch = false;
                                foreach (Cookie oReqCookie in this.oCookies)
                                {
                                    if (oReqCookie.Name == oRespCookie.Name)
                                    {
                                        oReqCookie.Value = oRespCookie.Value;
                                        bMatch = true;
                                        break; // 
                                    }
                                } // for each ReqCookies
                                if (!bMatch)
                                    this.oCookies.Add(oRespCookie);
                            } // for each Response.Cookies
                        }  // this.Cookies == null
                    } // if Response.Cookie.Count > 0
                }  // if this.bHandleCookies = 0


                // *** Save the response object for external access
                Encoding enc;
                try
                {
                    if (Response.ContentEncoding.Length > 0)
                        enc = Encoding.GetEncoding(Response.ContentEncoding);
                    else
                    {
                        enc = Encoding.GetEncoding(cEncoding);
                    }
                }
                catch
                {
                    // *** Invalid encoding passed
                    enc = Encoding.GetEncoding(cEncoding);
                }

                // *** drag to a stream
                StreamReader strResponse = new StreamReader(Response.GetResponseStream(), enc);
                string str = strResponse.ReadToEnd();
                Response.Close();
                strResponse.Close();
                //自动跟踪引用页
                if (this.bHandleReferer)
                {
                    this.cReferer = Url;
                }
                //自动处理HTTP/1.0 302 Moved Temporarily中的Location后的页面。（自动完成跳转）
                if (this.bLocation)
                {

                    //这里需要自动获得跳转页面的地址。并且再次使用这个方法访问页面
                }
                Response.Close();
                Request.Abort();
                return str;
            }
            catch (Exception e)
            {
                if (this.bThrowExceptions)
                    throw e;
                this.cErrorMsg = e.Message;
                this.bError = true;
                return null;
            }
        }


        public string GetUrlContent(string Url)
        {
            return GetUrlContent(Url, "", "", null);
        }


        public string GetUrlContent(string Url, string UserName, string Password, XmlDocument xmlDoc)
        {
            if (xmlDoc != null && xmlDoc.InnerXml != "")
                this.cPostData = xmlDoc.InnerXml;

            this.cUsername = UserName;
            this.cPassword = Password;

            Url = UrlEncode(Url);
            Debug.WriteLine(Url);
            try
            {
                this.bError = false;
                this.cErrorMsg = "";

                //通用的属性

                //使用 WebRequest.Create 方法初始化 HttpWebRequest 的一个新实例。
                //如果 URI 的方案是 http:// 或 https://，则 Create 将返回 HttpWebRequest 实例。

                HttpWebRequest Request = (HttpWebRequest)WebRequest.Create(Url);
                Request.UserAgent = this.cUserAgent;
                Request.Timeout = this.nConnectTimeout * 1000;
                Request.Accept = "*/*";

                Request.Referer = this.cReferer;

                //Request.Connection = "keep-alive";

                // 需要安全验证的访问
                if (this.cUsername.Length > 0)
                {
                    if (this.cUsername == "AUTOLOGIN")
                        Request.Credentials = CredentialCache.DefaultCredentials;
                    else
                        Request.Credentials = new NetworkCredential(this.cUsername, this.cPassword);
                }

                // 需要使用Proxy(代理服务器)和其配置
                if (this.cProxyAddress.Length > 0)
                {
                    if (this.cProxyAddress == "DEFAULTPROXY")
                    {
                        Request.Proxy = new WebProxy();
                        Request.Proxy = WebProxy.GetDefaultProxy();
                    }
                    else
                    {
                        WebProxy loProxy = new WebProxy(this.cProxyAddress, true);
                        if (this.cProxyBypass.Length > 0)
                        {
                            loProxy.BypassList = this.cProxyBypass.Split(';');
                        }

                        if (this.cProxyUsername.Length > 0)
                            loProxy.Credentials = new NetworkCredential(this.cProxyUsername, this.cProxyPassword);

                        Request.Proxy = loProxy;
                    }
                }

                // 需要操作Cookies和自动重用Cookies
                if (this.bHandleCookies)
                {
                    Request.CookieContainer = new CookieContainer();
                    if (this.oCookies != null && this.oCookies.Count > 0)
                    {
                        Request.CookieContainer.Add(this.oCookies);
                    }
                }

                Request.Method = cMethod;//设置提交模式

                if (this.cMethod == "POST")
                {
                    //内容类型
                    Request.ContentType = "application/x-www-form-urlencoded";//作为表单请求
                    //Request.ContentType = "text/html";

                    if (this.cPostData.EndsWith("&"))
                        this.cPostData = this.cPostData.Substring(0, this.cPostData.Length - 1);

                    //将URL编码后的字符串转化为字节 
                    byte[] lbPostBuffer = System.Text.Encoding.GetEncoding(cEncoding).GetBytes(cPostData);

                    //设置请求的ContentLength
                    Request.ContentLength = lbPostBuffer.Length;

                    //获得请求流
                    Stream loPostData = Request.GetRequestStream();

                    //将请求参数写入流
                    loPostData.Write(lbPostBuffer, 0, lbPostBuffer.Length);
                    loPostData.Close();

                    // *** clear the POST buffer
                    this.cPostData = "";
                }

                // *** Retrieve the response headers 

                //获得响应流
                HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
                //Request._ChallengedUri.m_AbsoluteUri
                string strName = Response.ResponseUri.AbsoluteUri.ToString();
                string[] arrName = strName.Split('/');
                string str = arrName[arrName.Length - 1].ToString();
                strUrlHeader = Response.Headers.ToString();
                Response.Close();
                Request.Abort();

                return str;
            }
            catch (System.Net.WebException ex)
            {
                if (ex.Status == System.Net.WebExceptionStatus.ProtocolError)
                {
                    try
                    {
                        return this.GetFileName(Url, this.cReferer);
                    }
                    catch (Exception ep)
                    {
                        throw new Exception("协议分析错误!" + ep.Message + ex.Message);
                    }
                }

                this.cErrorMsg = ex.Message;
                return null;
            }
            catch (Exception e)
            {
                if (this.bThrowExceptions)
                    throw e;
                this.cErrorMsg = e.Message;
                this.bError = true;
                return null;
            }
        }

        #endregion

        #region 内部函数
        private string UrlEncode(string url)
        {
            byte[] bs = Encoding.GetEncoding("gb2312").GetBytes(url);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < bs.Length; i++)
            {
                if (bs[i] < 128)
                    sb.Append((char)bs[i]);
                else
                {
                    sb.Append("%" + bs[i++].ToString("x").PadLeft(2, '0'));
                    sb.Append("%" + bs[i].ToString("x").PadLeft(2, '0'));
                }
            }
            return sb.ToString();
        }

        //发送socket请求
        private string GetFileName(string url, string refurl)
        {
            string[] urls = url.Split('/');

            string server = urls[2];

            url = url.ToLower().Replace("http://", "").Replace(server, "");

            Encoding ASCII = Encoding.ASCII;

            string Get = "GET " + url + " HTTP/1.1\r\n";
            Get += "Referer: " + refurl + "\r\n";
            Get += "Accept-Language: zh-cn\r\n";
            Get += "UA-CPU: x86\r\n";
            Get += "Accept-Encoding: gzip, deflater\n";
            Get += "User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; InfoPath.1; .NET CLR 2.0.50727)\r\n";
            Get += "Host: " + server + "\r\n";
            Get += "Connection: Keep-Alive\r\n";
            Get += "Cookie: cck_lasttime=1155823758000; cck_count=0; \r\n\r\n\r\n";

            Byte[] ByteGet = ASCII.GetBytes(Get);
            Byte[] RecvBytes = new Byte[256];
            string strRetPage = null;

            Socket s = connectSocket(server, 80);


            if (s == null)
                return ("Connection failed");

            s.Send(ByteGet, ByteGet.Length, 0);


            IPEndPoint sender = new IPEndPoint(IPAddress.Any, 0);
            EndPoint tempRemoteEP = (EndPoint)sender;

            Int32 bytes = s.Receive(RecvBytes, RecvBytes.Length, System.Net.Sockets.SocketFlags.Partial);


            strRetPage = "Default HTML page on " + server + ":\r\n";
            strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, bytes);


            if (bytes > 0)
            {
                bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);
                strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, bytes);
            }

            int iStart = strRetPage.IndexOf("Location:");
            int iEnd = strRetPage.IndexOf("\r\n", iStart);


            s.Close();

            return strRetPage.Substring(iStart + 9, iEnd - iStart - 9).Trim();

        }

        //建立socket
        private static Socket connectSocket(string server, int port)
        {
            Socket s = null;
            IPHostEntry iphe = null;

            iphe = Dns.Resolve(server);

            foreach (IPAddress ipad in iphe.AddressList)
            {
                IPEndPoint ipe = new IPEndPoint(ipad, port);

                Socket tmpS =
                    new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

                tmpS.Connect(ipe);

                if (tmpS.Connected)
                {
                    s = tmpS;
                    break;
                }
                else
                    continue;
            }

            return s;
        }

        #endregion
    }
}
